package com.wg.net.peer;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.wg.core.cache.CacheManager;
import com.wg.core.controller.ControllerMgr;
import com.wg.core.controller.IController;
import com.wg.core.func.Procedure;
import com.wg.core.thread.fiber.IFiber;
import com.wg.net.ConnectionState;
import com.wg.net.InnerErrorCode;
import com.wg.net.SendResult;
import com.wg.net.operations.InitRequest;
import com.wg.net.operations.PingRequest;
import com.wg.net.operations.PingResponse;
import com.wg.net.operations.base.OperationRequest;
import com.wg.net.operations.base.OperationResponse;
import com.wg.net.operations.base.S2SOperationRequest;
import com.wg.net.operations.base.S2SOperationResponse;
import com.wg.net.handle.HandleServerToServerRequestMgr;
import com.wg.net.handle.HandleServerToServerResponseMgr;
import com.wg.net.protocols.IProtocol;
import com.wg.net.protocols.MessageHeader;
import com.wg.net.protocols.MessageType;
import com.wg.net.session.ISession;

import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 服务器到服务器通信对端基类（处理S2S协议的消息收发与生命周期管理）
 * <p>
 * 职责：
 * - 管理S2S请求/响应的编解码与传输
 * - 处理请求超时、连接生命周期事件
 * - 提供控制器回调与异步任务调度支持
 *
 * @author 少爷123
 */
public abstract class BaseServerToServerPeer extends BasePeer {
    private static final Log log = LogFactory.get();

    /**
     * 请求缓存（键：请求ID，值：请求包装器；超时时间2分钟）
     */
    private final CacheManager<Integer, OperationWrapper> requestCache = new CacheManager<>(2, TimeUnit.MINUTES,
            key -> {
                log.warn("请求缓存自动清理（无显式删除）, key={}", key);
                return null;
            },
            this::handleRequestTimeout);

    /**
     * 请求ID生成器（线程安全）
     */
    private final AtomicInteger requestIDGenerator = new AtomicInteger(1);

    /**
     * 构造S2S对端（基于初始化请求）
     *
     * @param initRequest 初始化请求（包含连接参数）
     */
    protected BaseServerToServerPeer(InitRequest initRequest) {
        super(initRequest);
    }

    /**
     * 构造S2S对端（自定义协议、会话与状态）
     *
     * @param protocol        通信协议处理器
     * @param session         未管理会话对象
     * @param connectionState 连接状态
     */
    protected BaseServerToServerPeer(IProtocol protocol, ISession session,
                                     ConnectionState connectionState) {
        super(protocol, session, connectionState);
    }

    /**
     * 发送无响应的S2S操作请求
     *
     * @param request 待发送的S2S操作请求（不可为null）
     * @return 发送结果
     * @throws IllegalArgumentException 若request为null
     * @throws RuntimeException         若对端未初始化
     */
    public SendResult sendOperationRequest(S2SOperationRequest request) {
        return sendOperationRequest(request, (IFiber) null, null);
    }

    /**
     * 发送S2S操作请求（带控制器回调）
     *
     * @param request          待发送的S2S操作请求（不可为null）
     * @param controllerClass  处理响应的控制器类（可选）
     * @param responseCallback 响应回调（可选）
     * @return 发送结果
     * @throws IllegalArgumentException 若request为null
     * @throws RuntimeException         若对端未初始化
     */
    public SendResult sendOperationRequest(S2SOperationRequest request,
                                           Class<? extends IController> controllerClass,
                                           Procedure<S2SOperationResponse> responseCallback) {
        IController controller = ControllerMgr.getInstance().getController(controllerClass);
        IFiber fiber = (controller != null) ? controller.getFiber() : null;
        return sendOperationRequest(request, fiber, responseCallback);
    }

    /**
     * 发送S2S操作请求（完整参数版）
     *
     * @param request          待发送的S2S操作请求（不可为null）
     * @param fiber            异步任务调度纤维（可选）
     * @param responseCallback 响应回调（可选）
     * @return 发送结果
     * @throws IllegalArgumentException 若request为null
     * @throws RuntimeException         若对端未初始化
     */
    public SendResult sendOperationRequest(S2SOperationRequest request, IFiber fiber,
                                           Procedure<S2SOperationResponse> responseCallback) {
        Objects.requireNonNull(request, "S2SOperationRequest 不能为 null");
        checkConnectionState("发送请求");

        // 生成请求ID并序列化
        int requestId = generateRequestId();
        request.setId(requestId);
        byte[] data = serializeRequest(request, MessageType.Operation);
        if (data == null) {
            return SendResult.Failed;
        }

        // 处理带回调的请求（缓存包装器）
        if (responseCallback != null) {
            OperationWrapper wrapper = new OperationWrapper(request, fiber, responseCallback);
            requestCache.put(requestId, wrapper);

            SendResult sendResult = sendOperationRequestInternal(data, request);
            if (sendResult != SendResult.Ok) {
                S2SOperationResponse response = InnerErrorCode.NETWORK_SEND_FAIL.quickS2SResponse(request);
                handleResponse(response);
            }
            return sendResult;
        }

        return sendOperationRequestInternal(data, request);
    }

    @Override
    public SendResult sendOperationResponse(OperationResponse operationResponse) {
        // 参数校验
        Objects.requireNonNull(operationResponse, "OperationResponse 不能为 null");
        S2SOperationResponse s2sResponse = (S2SOperationResponse) operationResponse;
        Objects.requireNonNull(s2sResponse, "S2SOperationResponse 不能为 null");
        checkConnectionState("发送响应");

        // 序列化并发送
        byte[] data = serializeResponse(s2sResponse, MessageType.OperationResponse);
        if (data == null) {
            return SendResult.Failed;
        }

        SendResult sendResult = sendData(data);
        logDebugResponse(s2sResponse, data, sendResult);
        return sendResult;
    }

    @Override
    protected void onReceive(byte[] data) {
        if (data == null || data.length == 0) {
            log.warn("接收到空数据 | 连接ID={}", getConnectionId());
            return;
        }

        try {
            MessageHeader header = parseMessageHeader(data);
            if (header == null) {
                log.warn("消息头解析失败 | 连接ID={}, 数据长度={}", getConnectionId(), data.length);
                close();
                return;
            }

            processMessageByType(header, data);
        } catch (Exception e) {
            log.error("消息处理异常 | 连接ID={}, 数据长度={}", getConnectionId(), data.length, e);
            close(); // 异常时关闭连接
        }
    }

    /**
     * 处理超时请求（缓存清理回调）
     *
     * @param key              缓存键（请求ID）
     * @param operationWrapper 请求包装器
     * @param cause            移除原因
     */
    private void handleRequestTimeout(Integer key, OperationWrapper operationWrapper, RemovalCause cause) {
        if (operationWrapper == null || cause != RemovalCause.EXPIRED) {
            return;
        }

        log.warn("请求超时 | 连接ID={}, 请求ID={}", getConnectionId(), key);
        S2SOperationResponse timeoutResponse = InnerErrorCode.PROCESS_TIMEOUT.quickS2SResponse(operationWrapper.request);
        triggerResponseCallback(operationWrapper, timeoutResponse);
    }

    /**
     * 触发响应回调（异步执行）
     *
     * @param wrapper  请求包装器
     * @param response 响应对象
     */
    private void triggerResponseCallback(OperationWrapper wrapper, S2SOperationResponse response) {
        IFiber fiber = wrapper.fiber;
        Procedure<S2SOperationResponse> callback = wrapper.callback;

        if (callback == null) {
            return;
        }

        try {
            if (fiber != null) {
                fiber.enqueue(() -> callback.doSomething(response));
            } else {
                callback.doSomething(response);
            }
        } catch (Exception e) {
            log.error("回调执行异常 | 连接ID={}, 请求ID={}", getConnectionId(), wrapper.request.getId(), e);
        }
    }

    /**
     * 处理操作请求（内部逻辑）
     *
     * @param request S2S操作请求
     */
    @Override
    protected void onOperationRequest(OperationRequest request) {
        if (!(request instanceof S2SOperationRequest)) {
            log.warn("非S2S类型的操作请求 | 连接ID={}, 请求类型={}",
                    getConnectionId(), request.getClass().getSimpleName());
            return;
        }
        HandleServerToServerRequestMgr.getInstance().execute(this, (S2SOperationRequest) request);
    }

    /**
     * 处理操作响应（内部逻辑）
     *
     * @param response S2S操作响应
     */
    protected void onOperationResponse(S2SOperationResponse response) {
        HandleServerToServerResponseMgr.getInstance().execute(this, response);
    }

    /**
     * 发送内部操作响应（如Ping响应）
     *
     * @param response 内部操作响应对象
     */
    private void sendInternalOperationResponse(S2SOperationResponse response) {
        Objects.requireNonNull(response, "内部操作响应不能为 null");
        byte[] data = serializeResponse(response, MessageType.InternalOperationResponse);
        if (data == null) {
            return;
        }

        SendResult sendResult = sendData(data);
        logDebugResponse(response, data, sendResult);
    }

    /**
     * 检查连接状态（防止未初始化时操作）
     *
     * @param operation 操作描述（如"发送请求"）
     * @throws RuntimeException 若连接未初始化
     */
    private void checkConnectionState(String operation) {
        if (connectionState == ConnectionState.Initializing) {
            String errorMsg = StrUtil.format("连接未初始化，无法执行{} | 连接ID={}", operation, getConnectionId());
            log.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }
    }

    /**
     * 发送内部操作请求（如Ping请求）
     *
     * @param request 内部操作请求对象
     * @return 发送结果
     */
    protected SendResult sendInternalOperationRequest(S2SOperationRequest request) {
        byte[] data = serializeRequest(request, MessageType.InternalOperationRequest);
        if (data == null) {
            return SendResult.Failed;
        }
        return sendData(data);
    }

    /**
     * 发送操作请求（内部逻辑）
     *
     * @param data             发送数据
     * @param operationRequest 请求对象
     * @return 发送结果
     */
    private SendResult sendOperationRequestInternal(byte[] data, S2SOperationRequest operationRequest) {
        SendResult sendResult = sendData(data);
        if (log.isDebugEnabled()) {
            logDebugRequest(operationRequest, data, sendResult);
        }
        return sendResult;
    }

    /**
     * 生成请求ID（线程安全）
     *
     * @return 新请求ID
     */
    private int generateRequestId() {
        return requestIDGenerator.incrementAndGet();
    }

    /**
     * 序列化S2S操作请求
     *
     * @param request 请求对象
     * @param type    消息类型
     * @return 序列化后的字节数组（失败返回null）
     */
    private byte[] serializeRequest(S2SOperationRequest request, MessageType type) {
        try {
            return protocol.serializeS2SOperationRequest(request, type);
        } catch (Exception e) {
            log.error("请求序列化异常 | 连接ID={}, 请求ID={}", getConnectionId(), request.getId(), e);
            return null;
        }
    }

    /**
     * 序列化S2S操作响应
     *
     * @param response 响应对象
     * @param type     消息类型
     * @return 序列化后的字节数组（失败返回null）
     */
    private byte[] serializeResponse(S2SOperationResponse response, MessageType type) {
        try {
            return protocol.serializeS2SOperationResponse(response, type);
        } catch (Exception e) {
            log.error("响应序列化异常 | 连接ID={}, 响应ID={}", getConnectionId(), response.getId(), e);
            return null;
        }
    }

    /**
     * 记录调试日志（响应发送）
     *
     * @param response 响应对象
     * @param data     发送数据
     * @param result   发送结果
     */
    private void logDebugResponse(S2SOperationResponse response, byte[] data, SendResult result) {
        if (log.isDebugEnabled()) {
            log.debug("响应发送 | 连接ID={}, 响应ID={}, 结果={}, 数据大小={}字节",
                    getConnectionId(), response.getId(), result, data.length);
        }
    }

    /**
     * 记录调试日志（请求发送）
     *
     * @param request 请求对象
     * @param data    发送数据
     * @param result  发送结果
     */
    private void logDebugRequest(S2SOperationRequest request, byte[] data, SendResult result) {
        if (log.isDebugEnabled()) {
            log.debug("请求发送 | 连接ID={}, 请求ID={}, 结果={}, 数据大小={}字节",
                    getConnectionId(), request.getId(), result, data.length);
        }
    }

    /**
     * 解析消息头（带异常处理）
     *
     * @param data 原始字节数据
     * @return 消息头对象（解析失败返回null）
     */
    private MessageHeader parseMessageHeader(byte[] data) {
        try {
            return protocol.parseMessageHeader(data);
        } catch (Exception e) {
            log.error("消息头解析异常 | 连接ID={}, 数据: {}", getConnectionId(), StrUtil.sub(Arrays.toString(data), 0, 32), e);
            return null;
        }
    }

    /**
     * 根据消息类型处理接收数据
     *
     * @param header 消息头
     * @param data   原始数据
     */
    private void processMessageByType(MessageHeader header, byte[] data) {
        MessageType type = header.getMessageType();
        switch (type) {
            case Operation:
                handleOperationMessage(data, header);
                break;
            case OperationResponse:
                handleOperationResponse(data, header);
                break;
            case InternalOperationRequest:
                handleInternalOperationRequest(data, header.isZip());
                break;
            case InternalOperationResponse:
                handleInternalOperationResponse(data, header.isZip());
                break;
            default:
                log.warn("未知消息类型 | 连接ID={}, 类型={}", getConnectionId(), type);
                break;
        }
    }

    /**
     * 处理操作请求消息
     *
     * @param data   原始数据
     * @param header 消息头
     */
    private void handleOperationMessage(byte[] data, MessageHeader header) {
        S2SOperationRequest request = parseS2SRequest(data, header.isZip());
        if (request == null) {
            log.warn("操作请求解析失败 | 连接ID={}", getConnectionId());
            return;
        }
        onOperationRequest(request);
    }

    /**
     * 处理操作响应消息
     *
     * @param data   原始数据
     * @param header 消息头
     */
    private void handleOperationResponse(byte[] data, MessageHeader header) {
        S2SOperationResponse response = parseS2SResponse(data, header.isZip());
        if (response == null) {
            log.warn("操作响应解析失败 | 连接ID={}", getConnectionId());
            return;
        }
        handleResponse(response);
    }

    /**
     * 处理内部操作请求（如心跳）
     *
     * @param data  原始字节数据
     * @param isZip 是否压缩
     */
    private void handleInternalOperationRequest(byte[] data, boolean isZip) {
        S2SOperationRequest request = parseS2SRequest(data, isZip);
        if (request == null) {
            log.warn("内部操作请求解析失败 | 连接ID={}, 数据长度={}", getConnectionId(), data.length);
            onUnexpectedDataReceived(data, "内部操作请求解析失败");
            return;
        }
        handlePingRequest(request);
    }

    /**
     * 处理Ping请求（示例实现）
     *
     * @param request Ping请求对象
     */
    private void handlePingRequest(S2SOperationRequest request) {
        PingRequest pingReq = new PingRequest(request);
        PingResponse pingRes = new PingResponse(pingReq.getClientTimeStamp(), DateUtil.currentSeconds());
        S2SOperationResponse response = new S2SOperationResponse(request, pingRes);
        sendInternalOperationResponse(response);
    }

    private void handleInternalOperationResponse(byte[] data, boolean isZip) {
        S2SOperationResponse response = parseS2SResponse(data, isZip);
        if (response == null) {
            log.warn("操作响应解析失败 | 连接ID={} 数据: {}", getConnectionId(), Arrays.toString(data));
            return;
        }

        onInternalOperationResponse(response);
    }

    /**
     * 解析S2S操作请求（带异常处理）
     *
     * @param data  原始数据
     * @param isZip 是否压缩
     * @return S2S操作请求对象（解析失败返回null）
     */
    private S2SOperationRequest parseS2SRequest(byte[] data, boolean isZip) {
        try {
            return protocol.parseS2SOperationRequest(data, isZip);
        } catch (Exception e) {
            log.error("S2S操作请求解析异常 | 连接ID={}, 数据: {}", getConnectionId(), Arrays.toString(data), e);
            return null;
        }
    }

    /**
     * 解析S2S操作响应（带异常处理）
     *
     * @param data  原始数据
     * @param isZip 是否压缩
     * @return S2S操作响应对象（解析失败返回null）
     */
    private S2SOperationResponse parseS2SResponse(byte[] data, boolean isZip) {
        try {
            return protocol.parseS2SOperationResponse(data, isZip);
        } catch (Exception e) {
            log.error("S2S操作响应解析异常 | 连接ID={}, 数据: {}", getConnectionId(), Arrays.toString(data), e);
            return null;
        }
    }

    /**
     * 处理未知内部操作响应
     *
     * @param response 内部操作响应对象
     */
    private void onInternalOperationResponse(S2SOperationResponse response) {
        if (response.getReturnCode() != 0) {
            log.warn("内部操作响应异常 | 连接ID={}, 操作码={}", getConnectionId(), response.getOpCode());
        }
    }

    /**
     * 处理操作响应
     *
     * @param response 操作响应对象
     */
    private void handleResponse(S2SOperationResponse response) {
        OperationWrapper operationWrapper = requestCache.getIfPresent(response.getId());
        if (operationWrapper == null) {
            onOperationResponse(response);
            return;
        }
        requestCache.remove(response.getId());
        triggerResponseCallback(operationWrapper, response);
    }

    // ------------------------------ 内部类 ------------------------------

    /**
     * 请求包装器（存储请求元数据与回调）
     */
    private static class OperationWrapper {
        /**
         * 操作请求对象（不可变）
         */
        final S2SOperationRequest request;
        /**
         * 异步调度纤维（可能为null）
         */
        final IFiber fiber;
        /**
         * 响应回调（可能为null）
         */
        final Procedure<S2SOperationResponse> callback;

        /**
         * 构造请求包装器
         *
         * @param request  请求对象
         * @param fiber    异步纤维
         * @param callback 响应回调
         */
        OperationWrapper(S2SOperationRequest request, IFiber fiber,
                         Procedure<S2SOperationResponse> callback) {
            this.request = Objects.requireNonNull(request, "请求对象不能为 null");
            this.fiber = fiber;
            this.callback = callback;
        }
    }
}